/*
 *  Copyright (C) 2011 glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.commons;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import org.balisunrise.application.Application;
import org.balisunrise.application.Window;
import org.balisunrise.application.WindowsManager;
import org.balisunrise.builder.BuilderException;
import org.balisunrise.common.Basic;
import org.balisunrise.common.EntityFieldType;
import org.balisunrise.common.Event;
import org.balisunrise.common.PropertiesReader;
import org.balisunrise.common.Search;
import org.balisunrise.common.SelectionFieldLayout;
import org.balisunrise.common.SelectionPanelField;
import org.balisunrise.common.abstracts.AbstractFieldComponent;
import org.balisunrise.common.defaults.VEAUtil;
import org.balisunrise.common.listeners.FieldActionListener;
import org.balisunrise.swing.components.SelectionMessenger;

/**
 * Objetos desta interface implementam a ação de selecionar uma entidade.<br>
 * <br>
 * SelectionAction possui uma representação visual que ativa uma ação que
 * deve mostrar uma busca (<code>Search</code>) onde o usuário irá selecionar
 * uma entidade, a qual será atribuída ao <code>SelectionPanelGroup</code>.<br>
 * O <code>Search</code> é construído utilizando o SearchBuilder do Application
 * e mostrado na tela pelo ViewManager.
 * Se nenhuma entidade for selecionada depois de mostrar a busca, é mostrado
 * a mensagem de notFoundMessage.<br>
 * Se foi selecionado alguma entidade o Focus deve ir para o último componente
 * Criterionable do selectionPanelGroup, caso contrário deve ir para primeiro
 * componente Criterionable.<br>
 * <br>
 * SelectionAction também implementa a ação de selecionar uma entidade ao focus
 * pelo último componente Criterionable do selectionPanelGroup.<br>
 * Esta segunda ação deve ser disparada só deve ser disparada quando algum
 * Criterionable for modificado.<br>
 * O SelectionAction constrói um <code>EntitySelection</code> utilizando o
 * Builder do Application e mostra na tela pelo ViewManager.<br>
 * As condições para listagem do EntitySelection são os Criterionable do
 * selectionPanelGroup.<br>
 * Caso a listagem não retorne resultados, deve ser mostrada a mensagem de
 * notFoundMessage.<br>
 * Caso a listagem retorne apenas um resultado, ele deve ser atribuído
 * diretamente ao <code>SelectionPanelGroup</code> e não deve ser mostrado o
 * <code>EntitySelection</code>. As outras condições da seleção da entidade pelo
 * <code>EntitySelection</code> são as mesmas do <code>Search</code>.
 *
 * @author glauco
 * @see org.balisunrise.common.Search
 * @see org.balisunrise.application.Application
 * @see org.balisunrise.builder.SearchBuilder
 * @see org.balisunrise.common.SelectionPanelGroup
 * @see org.balisunrise.application.ViewManager
 * @see org.balisunrise.common.Criterionable
 * @see org.balisunrise.common.EntitySelection
 */
public class SelectionAction extends AbstractFieldComponent
        implements org.balisunrise.common.SelectionAction<JLabel>,
        Runnable{

    private static final String PROP_VISUAL_REPRESENTATION = "visualRepresentation";
    private static final String PROP_SELECTION_FIELD_LAYOUT = "selectionFieldLayout";
    private static final String PROP_SEARCH_ALIAS = "searchAlias";
    private static final String PROP_NOT_FOUND_MESSAGE = "notFoundMessage";

    private JLabel visualRepresentation;
    private SelectionFieldLayout selectionFieldLayout;
    private String searchAlias;
    private Search search;
    private Window searchWindow;
    private String notFoundMessage;
    private SelectionMessenger selectionMessenger;
    private List<FieldActionListener> listeners;

    public SelectionAction() {
        listeners = new ArrayList<FieldActionListener>();
    }

    /**
     * Set NotFoundMessage
     */
    public void setNotFoundMessage(String notFoundMessage) {
        String old = this.notFoundMessage;
        this.notFoundMessage = notFoundMessage;
        changeSupport.firePropertyChange(PROP_NOT_FOUND_MESSAGE, old,
                notFoundMessage);
    }

    /**
     * Get NotFoundMessage
     */
    public void setSearchAlias(String searchAlias) {
        String old = this.searchAlias;
        this.searchAlias = searchAlias;
        changeSupport.firePropertyChange(PROP_SEARCH_ALIAS, old, searchAlias);
    }

    /**
     * Set VisualRepresentation
     */
    public void setVisualRepresentation(JLabel visualRepresentation) {
        JLabel old = this.visualRepresentation;
        this.visualRepresentation = visualRepresentation;
        changeSupport.firePropertyChange(PROP_VISUAL_REPRESENTATION,
                old, visualRepresentation);
    }

    /**
     * Get SelectionFieldLayout
     */
    public SelectionFieldLayout getSelectionFieldLayout() {
        return selectionFieldLayout;
    }

    /**
     * Retorna o alias da busca.
     * @param searchAlias
     */
    @Override
    public String getSearchAlias() {
        return searchAlias;
    }

    /**
     * Atribui o grupo de seleção.
     * @param selectionFieldLayout
     */
    @Override
    public void setSelectionFieldLayout(SelectionFieldLayout sfl) {
        SelectionFieldLayout old = this.selectionFieldLayout;
        this.selectionFieldLayout = sfl;
        changeSupport.firePropertyChange(PROP_SELECTION_FIELD_LAYOUT, old, sfl);
    }

    /**
     * Operação não suportada.
     * @return false.
     */
    @Override
    public boolean requestFocus() {
        return false;
    }

    /**
     * Representação Visual do icone de busca.
     * @return
     */
    @Override
    public JLabel getVisualRepresentation() {
        return visualRepresentation;
    }

    /**
     * Retorna Representação Visual.
     * @return
     */
    @Override
    public EntityFieldType getEntityFieldType() {
        return EntityFieldType.COMPONENT;
    }

    /**
     * Não aceita está operação.
     * @return false.
     */
    @Override
    public boolean setEnabled(boolean bln) {
        return false;
    }

    /**
     * Get notFoundMessage;
     */
    @Override
    public String getNotFoundMessage() {
        return notFoundMessage;
    }

    /**
     * Chama a pesquisa de seleção.
     */
    @Override
    public void perform(Event event) {

        clearMessage();
        WindowsManager wm = Application.get().getWindowsManager();

        if(searchWindow == null)
            try {
                search = Application.get()
                        .getVisualEntityManager()
                        .getSearchBuilder()
                        .create(getFieldClass(), searchAlias);
                search.setOwner(this);
                searchWindow = wm.createWindow(search);
                searchWindow.setOwner(this);
            } catch (BuilderException ex) {
                String msg =
                        "Falha ao construir Search em SelectionAction.";
                Application.get().getLogger().log(Level.SEVERE, msg, ex);
                return ;
            }

        // Mostra a window
        Application.get().getWindowsManager().showWindow(searchWindow);

        if(searchWindow.getConfirmation()){
            selectionFieldLayout.setEntity(search.getSelectedEntity());
            selectionFieldLayout.requestFocus();
        }else{
            selectionFieldLayout.setEntity(null);
            showNotFoundMessage();
        }
        
        // FIELD ACTION EVENT
        for (FieldActionListener fal : listeners) {
            fal.fieldActionPerformed(event);
        }
        getOwner().actionPerformed(event);

        SwingUtilities.invokeLater(this);
    }

    /**
     * Seta o Focus para PanelField Owner deste.
     */
    @Override
    public void run() {
        if(owner != null && owner
                instanceof org.balisunrise.common.FieldLayout){

            org.balisunrise.common.FieldLayout fl =
                    (org.balisunrise.common.FieldLayout) owner;

            fl.requestFocus();
        }
    }

    private SelectionMessenger getSelectionMessenger(){
        if(selectionMessenger == null){
            Basic b = VEAUtil.getFromBasic(this, SelectionPanelField.class);
            if(b != null && b instanceof SelectionPanelField)
                selectionMessenger = SelectionMessenger
                        .getInstance((SelectionPanelField) b);
        }
        return selectionMessenger;
    }

    /**
     * Remove a mensagem do messenger
     */
    public void clearMessage(){
        getSelectionMessenger().clearMessage();
    }

    /**
     * Mostra mensagem de que não há nenhum registro para a entrada da seleção.
     */
    private void showNotFoundMessage(){

        if(notFoundMessage == null || notFoundMessage.isEmpty())
            setNotFoundMessage(Application.get()
                    .getPropertiesReader()
                    .readProperty(PropertiesReader.messageINVALID_INPUT));

        getSelectionMessenger().putMessage(notFoundMessage);

        selectionFieldLayout.requestFocus();
    }

    @Override
    public void addFieldActionListener(FieldActionListener fl) {
        listeners.add(fl);
    }

    @Override
    public boolean removeFieldActionListener(FieldActionListener fl) {
        return listeners.remove(fl);
    }

    @Override
    public FieldActionListener[] getFieldActionListeners() {
        return listeners.toArray(new FieldActionListener[]{});
    }
}
